<template>
  <div class="hello">
    <div style="display: flex">
      <button @click="startMeasure">测距</button>
      <button @click="stopMeasure">清除</button>
    </div>
    <div id="map" class="map" tabindex="0"></div>
    <TyphoonInfo ref="typhoonInfo" :typhoonData="this.typhoonData" />
  </div>
</template>

<script>
import "ol/ol.css";
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import { getTyphoonData } from "../data/api";
import { Vector as VectorLayer } from "ol/layer";
import { WMTS as WMTS, Vector as VectorSource } from "ol/source";
import Feature from "ol/Feature";
import { getTopLeft, getWidth } from "ol/extent";
import { fromLonLat, get as getProjection } from "ol/proj";
import { Point, MultiLineString, LineString, Polygon, Circle } from "ol/geom";
import { Fill, Circle as CircleStyle, Style, Stroke } from "ol/style";
import featureObj from "../common/js/feature";
import WMTSTileGrid from "ol/tilegrid/WMTS";
import TyphoonInfo from "../components/typhoonInfo.vue";
import Overlay from "ol/Overlay";
import Draw from "ol/interaction/Draw";
import { getLength } from "ol/sphere";
import { unByKey } from "ol/Observable";
export default {
  name: "HelloWorld",
  data() {
    return {
      map: null,
      lastShowSolar: null,
      lastZoomPoint: null,
      typhoonData: {},
      tfOverlay: null,
      drawLayers: [],
      drawDoms: [],
    };
  },
  components: {
    TyphoonInfo,
  },
  methods: {
    // 画出台风路径
    drawTyphoonPath: async function () {
      const { data } = await getTyphoonData();
      console.log(data);
      let points = data.points;
      let features = [];

      let positions = [];
      for (let i = 0; i < points.length; i++) {
        let position = [points[i].lng, points[i].lat];
        let featurePoint = new Feature({
          geometry: new Point(fromLonLat(position)),
        });
        featurePoint.setStyle(
          new Style({
            image: new CircleStyle({
              fill: new Fill({
                color: this.judgeColorByWindLevel(points[i].strong),
              }),
              radius: 4,
            }),
          })
        );
        featurePoint.set("typhoonPoint", true);
        if (i != points.length - 1) {
          let nextPosition = [points[i + 1].lng, points[i + 1].lat];
          positions.push([fromLonLat(position), fromLonLat(nextPosition)]);
        }
        features.push(featurePoint);
      }
      let featureLine = new Feature({
        geometry: new MultiLineString(positions),
      });
      features.push(featureLine);
      // 矢量图层
      let layer = new VectorLayer();
      // 矢量数据源
      let source = new VectorSource();
      source.addFeatures(features);
      layer.setSource(source);
      this.map.addLayer(layer);
    },
    // 画出台风路径 interval版
    drawTyphoonPathInterval: async function () {
      const { data } = await getTyphoonData();
      console.log(data);
      let points = data.points;
      let index = 0;
      let layer = new VectorLayer();
      let source = new VectorSource();
      layer.setSource(source);
      let intervalLogo = setInterval(() => {
        if (index == points.length) {
          clearInterval(intervalLogo);
          return;
        }
        let position = [points[index].lng, points[index].lat];
        if (index == 0) {
          this.map.getView().setCenter(fromLonLat(position));
        }
        // 点 的 数据源设置
        let featurePoint = new Feature({
          geometry: new Point(fromLonLat(position)),
        });
        featurePoint.setStyle(
          new Style({
            image: new CircleStyle({
              fill: new Fill({
                color: this.judgeColorByWindLevel(points[index].strong),
              }),
              radius: 4,
            }),
          })
        );
        featurePoint.set("typhoonPoint", true);
        featurePoint.set("points", points[index]);
        // 写到这里的时候 预期的结果 应该是 如果有 全部都绘制了  、 没有清除  上一个被绘制的目标点
        if (
          points[index].radius7.length != 0 ||
          points[index].radius7 != null
        ) {
          let featureSolar = this.drawSolarExact(points[index]);
          let lastShowSolar = this.lastShowSolar;
          if (lastShowSolar != null) {
            source.removeFeature(lastShowSolar);
          }
          this.lastShowSolar = featureSolar;
          source.addFeature(featureSolar);
        }

        source.addFeature(featurePoint);

        if (index > 0) {
          let lastPosition = [points[index - 1].lng, points[index - 1].lat];
          let featureLine = new Feature({
            geometry: new LineString([
              fromLonLat(position),
              fromLonLat(lastPosition),
            ]),
          });
          source.addFeature(featureLine);
        }
        // 需要一个出口
        index++;
      }, 100);
      this.map.addLayer(layer);
    },

    // 注册一个 hover 事件
    designHoverOnMap: function () {
      let _this = this;
      this.map.on("pointermove", function (ev) {
        let pixel = ev.pixel;
        let feature = _this.map.forEachFeatureAtPixel(
          pixel,
          function (feature) {
            return feature;
          }
        );
        if (feature) {
          let execName = featureObj.typeJudge(feature) + "Hover";
          featureObj[execName].apply(_this, [feature]);
        } else {
          _this.clearPointZoomStyle();
          _this.map.getTargetElement().style.cursor = "";
          _this.tfOverlay.setPosition(undefined);
        }
      });
    },

    designClickOnMap: function () {
      let _this = this;
      this.map.on("click", function (ev) {
        let pixel = ev.pixel;
        let feature = _this.map.forEachFeatureAtPixel(
          pixel,
          function (feature) {
            return feature;
          }
        );
        if (feature) {
          let execName = featureObj.typeJudge(feature) + "Click";
          featureObj[execName].apply(_this, [feature]);
        } else {
          console.log("no feature");
        }
      });
    },

    // clearPoint Zoom Style
    clearPointZoomStyle: function () {
      let _this = this;
      if (_this.lastZoomPoint != null) {
        _this.lastZoomPoint.getStyle().getImage().setRadius(4);
        _this.lastZoomPoint.changed();
      }
    },

    // drawSolar 绘制风圈
    /**
     * 应该是明确的只让他返回 一个 feature
     */
    drawSolar: function (points) {
      let radiusArr = points.radius7.split("|").map((item) => {
        return parseFloat(item);
      });
      var Configs = {
        CIRCLE_CENTER_X: parseFloat(points.lng),
        CIRCLE_CENTER_Y: parseFloat(points.lat),
        CIRCLE_R: {
          SE: radiusArr[0] / 100,
          NE: radiusArr[1] / 100,
          NW: radiusArr[2] / 100,
          SW: radiusArr[3] / 100,
        },
      };
      let positions = [];
      var _interval = 6;
      for (var i = 0; i < 360 / _interval; i++) {
        var _r = 0;
        var _ang = i * _interval;
        if (_ang > 0 && _ang <= 90) {
          _r = Configs.CIRCLE_R.NE;
        } else if (_ang > 90 && _ang <= 180) {
          _r = Configs.CIRCLE_R.NW;
        } else if (_ang > 180 && _ang <= 270) {
          _r = Configs.CIRCLE_R.SW;
        } else {
          _r = Configs.CIRCLE_R.SE;
        }

        var x = Configs.CIRCLE_CENTER_X + _r * Math.cos((_ang * 3.14) / 180);
        var y = Configs.CIRCLE_CENTER_Y + _r * Math.sin((_ang * 3.14) / 180);
        positions.push(fromLonLat([x, y]));
      }
      let feature = new Feature({
        geometry: new Polygon([positions]),
      });
      feature.set("typhoonSolar", true);
      return feature;
    },

    // 准确的绘制台风风圈
    drawSolarExact: function (points) {
      let radiusArr = points.radius7.split("|").map((item) => {
        return parseFloat(item);
      });

      // south  南  East 东  notrh 北 west 西
      var configs = {
        SE: radiusArr[0] * 1000,
        SW: radiusArr[1] * 1000,
        NW: radiusArr[2] * 1000,
        NE: radiusArr[3] * 1000,
      };
      const circleFeature = new Feature({
        geometry: new Circle(fromLonLat([points.lng, points.lat])),
      });

      circleFeature.setStyle(
        new Style({
          renderer(coordinates, state) {
            let ctx = state.context;
            let [x, y] = coordinates[0];
            let count = 1;
            ctx.beginPath();
            // let []
            for (let i in configs) {
              let degree = 0.5 * Math.PI * count;
              let distance = configs[i] / state.resolution;
              ctx.arc(x, y, distance, degree - 0.5 * Math.PI, degree);
              count++;
            }
            ctx.fillStyle = "rgba(238, 160, 29,0.4)";
            ctx.fill();
            ctx.closePath();
            ctx.strokeStyle="rgb(238, 160, 29)";
            ctx.stroke();
          },
        })
      );
      circleFeature.set("typhoonSolar", true);
      return circleFeature;
    },

    // 测距功能
    startMeasure: function () {
      const source = new VectorSource();

      const layer = new VectorLayer({
        source: source,
        style: new Style({
          fill: new Fill({
            color: "rgba(255, 255, 255, 0.2)",
          }),
          stroke: new Stroke({
            color: "#ffcc33",
            width: 2,
          }),
          image: new CircleStyle({
            radius: 7,
            fill: new Fill({
              color: "#ffcc33",
            }),
          }),
        }),
      });
      let _this = this;

      let feature;
      // overlay
      let helpTooltip;
      let measureTooltip;
      // dom节点
      let helpTooltipElement;
      let measureTooltipElement;
      // 交互类 绘制
      let draw;
      let listener;

      let pointermoveListener = this.map.on("pointermove", function (ev) {
        let helpMsg = "点击地图作为目标点";
        if (feature) {
          helpMsg = "双击地图作为结束点";
        }
        helpTooltipElement.innerHTML = helpMsg;
        helpTooltip.setPosition(ev.coordinate);
        helpTooltipElement.classList.remove("hidden");
      });

      draw = new Draw({
        source,
        type: "LineString",
        style: new Style({
          fill: new Fill({
            color: "rgba(255, 255, 255, 0.2)",
          }),
          stroke: new Stroke({
            color: "rgba(0, 0, 0, 0.5)",
            lineDash: [10, 10],
            width: 2,
          }),
          image: new CircleStyle({
            radius: 5,
            stroke: new Stroke({
              color: "rgba(0, 0, 0, 0.7)",
            }),
            fill: new Fill({
              color: "rgba(255, 255, 255, 0.2)",
            }),
          }),
        }),
      });

      // 监听开始绘制 、
      draw.on("drawstart", function (evt) {
        // set sketch
        feature = evt.feature;

        /** @type {import("../src/ol/coordinate.js").Coordinate|undefined} */
        let tooltipCoord = evt.coordinate;

        listener = feature.getGeometry().on("change", function (evt) {
          const geom = evt.target;
          let output = formatLength(geom);
          tooltipCoord = geom.getLastCoordinate();
          measureTooltipElement.innerHTML = output;
          measureTooltip.setPosition(tooltipCoord);
        });
      });

      this.map.getViewport().addEventListener("mouseout", function () {
        helpTooltipElement.classList.add("hidden");
      });

      draw.on("drawend", function () {
        measureTooltipElement.className = "ol-tooltip ol-tooltip-static";
        measureTooltip.setOffset([0, -7]);
        // unset sketch
        feature = null;
        // unset tooltip so that a new one can be created
        measureTooltipElement = null;
        createMeasureTooltip();
        _this.map.removeInteraction(draw);
        unByKey(listener);
        unByKey(pointermoveListener);
      });

      // 格式化长度
      const formatLength = function (line) {
        const length = getLength(line);
        let output;
        if (length > 100) {
          output = Math.round((length / 1000) * 100) / 100 + " " + "km";
        } else {
          output = Math.round(length * 100) / 100 + " " + "m";
        }
        return output;
      };

      function createHelpTooltip() {
        if (helpTooltipElement) {
          helpTooltipElement.parentNode.removeChild(helpTooltipElement);
        }
        helpTooltipElement = document.createElement("div");
        helpTooltipElement.className = "ol-tooltip hidden";
        helpTooltip = new Overlay({
          element: helpTooltipElement,
          offset: [15, 0],
          positioning: "center-left",
        });
        _this.map.addOverlay(helpTooltip);
      }
      function createMeasureTooltip() {
        if (measureTooltipElement) {
          measureTooltipElement.parentNode.removeChild(measureTooltipElement);
        }
        measureTooltipElement = document.createElement("div");
        measureTooltipElement.className = "ol-tooltip ol-tooltip-measure";
        measureTooltip = new Overlay({
          element: measureTooltipElement,
          offset: [0, -15],
          positioning: "bottom-center",
          stopEvent: false,
          insertFirst: false,
        });
        _this.drawDoms.push(measureTooltipElement);
        _this.map.addOverlay(measureTooltip);
      }
      createHelpTooltip();
      createMeasureTooltip();

      this.map.addInteraction(draw);
      this.drawLayers.push(layer);
      this.map.addLayer(layer);
    },

    // 清除
    stopMeasure: function () {
      let layers = this.drawLayers;
      console.log(this.drawDoms);
      for(let i = 0;i < layers.length;i++){
        this.map.removeLayer(layers[i]);
      }
      for(let i = 0;i < this.drawDoms.length;i++){
        this.drawDoms[i].parentNode.removeChild(this.drawDoms[i]);
      }
    },

    // 设置弹窗 位置
    setTFINFOPostion: function (points) {
      let position = fromLonLat([points.lng, points.lat]);
      this.tfOverlay.setPosition(position);
    },

    // 添加叠加层
    addOverlay: function () {
      const overlay = new Overlay({
        element: this.$refs.typhoonInfo.$el,
        autoPan: true,
        autoPanAnimation: {
          duration: 250,
        },
      });
      overlay.setPosition(undefined);
      this.tfOverlay = overlay;
      this.map.addOverlay(overlay);
    },

    /**
     * @param {index} 想要初始化的地图类型的相关索引 支持：矢量图、影像图、地形图，分别对应 0 1 2
     * @param {key} 天地图 中申请的秘钥key
     * @param {*} centerPosition 中心点的经纬度坐标
     * @notice 参数不能为空
     */
    tiandituInit(index, key) {
      // 如果传进来的 map2d的参数为null，说明需要新建一个map地图
      if (this.map == null) {
        let layerArr = this.JudgeBaseAndNoteByType(index);
        // 地图注记 与 底图的相关配置
        console.log(layerArr);
        var projection = getProjection("EPSG:3857");
        var projectionExtent = projection.getExtent();
        var size = getWidth(projectionExtent) / 256;
        var resolutions = new Array(14);
        var matrixIds = new Array(14);
        for (var z = 0; z < 14; ++z) {
          // generate resolutions and matrixIds arrays for this WMTS
          resolutions[z] = size / Math.pow(2, z);
          matrixIds[z] = z;
        }
        let layer_Base = new TileLayer({
          opacity: 1,
          source: new WMTS({
            url:
              "http://t{0-7}.tianditu.gov.cn/" +
              layerArr[0] +
              "_w/wmts?tk=" +
              key,
            layer: layerArr[0],
            matrixSet: "w",
            format: "tiles",
            style: "default",
            tileGrid: new WMTSTileGrid({
              origin: getTopLeft(projectionExtent),
              resolutions: resolutions,
              matrixIds: matrixIds,
            }),
            wrapX: true,
          }),
          visible: true,
        });
        let layer_Note = new TileLayer({
          opacity: 1,
          source: new WMTS({
            url:
              "http://t{0-7}.tianditu.gov.cn/" +
              layerArr[1] +
              "_w/wmts?tk=" +
              key,
            layer: layerArr[1],
            matrixSet: "w",
            format: "tiles",
            style: "default",
            tileGrid: new WMTSTileGrid({
              origin: getTopLeft(projectionExtent),
              resolutions: resolutions,
              matrixIds: matrixIds,
            }),
            wrapX: true,
          }),
          visible: true,
        });
        var map = new Map({
          layers: [layer_Base, layer_Note],
          target: "map",
          view: new View({
            center: fromLonLat([100, 20]),
            zoom: 5,
            minZoom: 4,
          }),
        });
        console.log(map);
        this.map = map;
      }
    },

    // 根据 约定的 类型的索引 判别 底图 与 注记图 需要渲染的layer类型
    JudgeBaseAndNoteByType(index) {
      let map = [
        ["vec", "cva"],
        ["ter", "cta"],
        ["img", "cia"],
      ];
      return map[index];
    },

    /**
     * 根据台风等级判断颜色
     * @param {String} windlevel 台风等级描述
     * @return {String} map[windlevel] color Name
     */
    judgeColorByWindLevel: function (windlevel) {
      let map = {
        热带风暴: "red",
        热带低压: "blue",
        强热带风暴: "green",
        台风: "orange",
        强台风: "yellow",
        超强台风: "salmon",
      };

      return map[windlevel];
    },
  },
  created() {},
  mounted() {
    this.tiandituInit(0, "11748a73773880cc126562dfe4cf9047");
    // this.drawTyphoonPath();
    this.drawTyphoonPathInterval();
    this.designHoverOnMap();
    this.designClickOnMap();
    this.addOverlay();
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
.map {
  width: 100%;
  height: 100vh;
}

#map:focus {
  outline: #4a74a8 solid 0.15em;
}
.hidden{
  display: none;
}
.ol-tooltip {
  position: relative;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 4px;
  color: white;
  padding: 4px 8px;
  opacity: 0.7;
  white-space: nowrap;
  font-size: 12px;
  cursor: default;
  user-select: none;
}
.ol-tooltip-measure {
  opacity: 1;
  font-weight: bold;
}
.ol-tooltip-static {
  background-color: #ffcc33;
  color: black;
  border: 1px solid white;
}
.ol-tooltip-measure:before,
.ol-tooltip-static:before {
  border-top: 6px solid rgba(0, 0, 0, 0.5);
  border-right: 6px solid transparent;
  border-left: 6px solid transparent;
  content: "";
  position: absolute;
  bottom: -6px;
  margin-left: -7px;
  left: 50%;
}
.ol-tooltip-static:before {
  border-top-color: #ffcc33;
}
</style>
